package eval

import (
	"showen/lex"
	"showen/obj"
	"showen/parse"
	"testing"
)

func TestTypePredicates(t *testing.T) {
	tests := []struct {
		input    string
		expected bool
	}{
		// boolean? tests
		{"(boolean? #t)", true},
		{"(boolean? #f)", true},
		{"(boolean? 1)", false},
		{"(boolean? 'symbol)", false},

		// pair? tests
		{"(pair? (cons 1 2))", true},
		{"(pair? '())", false},
		{"(pair? 1)", false},

		// symbol? tests
		{"(symbol? 'symbol)", true},
		{"(symbol? 1)", false},
		{"(symbol? #t)", false},

		// number? tests
		{"(number? 1)", true},
		{"(number? 1/2)", true},
		{"(number? 1.5)", true},
		{"(number? 1+2i)", true},
		{"(number? 'symbol)", false},

		// integer? tests
		{"(integer? 1)", true},
		{"(integer? 1/2)", false},
		{"(integer? 1.5)", false},

		// rational? tests
		{"(rational? 1/2)", true},
		{"(rational? 1)", true},
		{"(rational? 1.5)", false},

		// real? tests
		{"(real? 1.5)", true},
		{"(real? 1)", true},
		{"(real? 1/2)", true},

		// complex? tests
		{"(complex? 1+2i)", true},
		{"(complex? 1)", true},

		// char? tests
		{"(char? #\\a)", true},
		{"(char? 1)", false},

		// string? tests
		{"(string? \"hello\")", true},
		{"(string? 1)", false},

		// procedure? tests
		{"(procedure? +)", true},
		{"(procedure? 'symbol)", false},
	}

	for _, tt := range tests {
		lexer := lex.NewLexer(tt.input)
		tokens := lexer.ReadTokens()
		parser := parse.NewParser(tokens)
		program, err := parser.ParseProgram()
		if err != nil {
			t.Fatalf("ParseProgram() error = %v", err)
		}

		env := obj.NewEnvironment()
		RegisterBuiltinProcedures(env)

		var result obj.Object
		for _, expr := range program {
			result, err = EvalExpression(expr, env)
			if err != nil {
				t.Fatalf("EvalExpression() error = %v", err)
			}
		}

		if result.Type() != obj.BOOLEAN_OBJ {
			t.Errorf("Expected boolean result for input %q, got %d", tt.input, result.Type())
			continue
		}

		booleanResult := result.(*obj.BooleanObject)
		if booleanResult.Value != tt.expected {
			t.Errorf("For input %q, expected %v, got %v", tt.input, tt.expected, booleanResult.Value)
		}
	}
}

func TestListAndPairIntegration(t *testing.T) {
	tests := []struct {
		input    string
		expected string
	}{
		{"(list)", "()"}, // Empty list is represented as empty string in Inspect()
		{"(list 1)", "(1)"},
		{"(list 1 2 3)", "(1 2 3)"},
		{"(pair? (list 1 2))", "#t"},
		{"(pair? 1)", "#f"},
		{"(pair? '())", "#f"},
		{"(pair? (cons 1 2))", "#t"},
	}

	env := obj.NewEnvironment()
	RegisterBuiltinProcedures(env)

	for _, tt := range tests {
		evaluated := testEvalList(tt.input, env, t)
		if evaluated.Inspect() != tt.expected {
			t.Errorf("list/pair test failed: input=%s, expected=%s, got=%s",
				tt.input, tt.expected, evaluated.Inspect())
		}
	}
}

func testEvalList(input string, env *obj.Environment, t *testing.T) obj.Object {
	l := lex.NewLexer(input)
	tokens := l.ReadTokens()
	p := parse.NewParser(tokens)

	// Try to parse as program first
	program, err := p.ParseProgram()
	if err != nil {
		// If that fails, try parsing as expression
		expr, err := p.ParseExpression()
		if err != nil {
			t.Fatalf("ParseExpression failed: %s", err)
		}
		evaluated, err := EvalExpression(expr, env)
		if err != nil {
			t.Fatalf("EvalExpression failed: %s", err)
		}
		return evaluated
	}
	evaluated, err := EvalProgram(program, env)
	if err != nil {
		t.Fatalf("EvalProgram failed: %s", err)
	}
	return evaluated
}

func TestCharacterOperations(t *testing.T) {
	tests := []struct {
		input string
		want  string
	}{
		// Character comparison
		{"(char=? #\\a #\\a)", "#t"},
		{"(char=? #\\a #\\b)", "#f"},
		{"(char<? #\\a #\\b)", "#t"},
		{"(char>? #\\b #\\a)", "#t"},
		{"(char<=? #\\a #\\a)", "#t"},
		{"(char>=? #\\b #\\b)", "#t"},

		// Case-insensitive character comparison
		{"(char-ci=? #\\a #\\A)", "#t"},
		{"(char-ci<? #\\a #\\B)", "#t"},
		{"(char-ci>? #\\B #\\a)", "#t"},

		// Character predicates
		{"(char-alphabetic? #\\a)", "#t"},
		{"(char-alphabetic? #\\5)", "#f"},
		{"(char-numeric? #\\5)", "#t"},
		{"(char-numeric? #\\a)", "#f"},
		{"(char-whitespace? #\\ )", "#t"},
		{"(char-whitespace? #\\a)", "#f"},
		{"(char-upper-case? #\\A)", "#t"},
		{"(char-upper-case? #\\a)", "#f"},
		{"(char-lower-case? #\\a)", "#t"},
		{"(char-lower-case? #\\A)", "#f"},

		// Character conversion
		{"(char->integer #\\a)", "97"},
		{"(integer->char 97)", "#\\a"},

		// Character case conversion
		{"(char-upcase #\\a)", "#\\A"},
		{"(char-downcase #\\A)", "#\\a"},
	}

	env := obj.NewEnvironment()
	RegisterBuiltinProcedures(env)

	for _, tt := range tests {
		lexer := lex.NewLexer(tt.input)
		tokens := lexer.ReadTokens()
		parser := parse.NewParser(tokens)
		expr, err := parser.ParseExpression()
		if err != nil {
			t.Fatalf("parser.ParseExpression() error = %v", err)
		}

		obj, err := EvalExpression(expr, env)
		if err != nil {
			t.Fatalf("Eval() error = %v", err)
		}
		if obj.Inspect() != tt.want {
			t.Errorf("Eval() = %v, want %v", obj.Inspect(), tt.want)
		}
	}
}

func TestQuoteIntegration(t *testing.T) {
	tests := []struct {
		input string
		want  string
	}{
		{"'1", "1"},
		{"'#t", "#t"},
		{"'hello", "hello"},
		{"'(1 2 3)", "(1 2 3)"},
		{"'((1 2) 3)", "((1 2) 3)"},
		{"\"hello\"", "\"hello\""},
		{"'\"hello\"", "\"hello\""},
		{"'(1 . 2)", "(1 . 2)"},
		{"'(+ 1 2)", "(+ 1 2)"},
		{"'(quote (1 2 3))", "(quote (1 2 3))"},
	}

	env := obj.NewEnvironment()
	RegisterBuiltinProcedures(env)

	for _, tt := range tests {
		lexer := lex.NewLexer(tt.input)
		tokens := lexer.ReadTokens()
		parser := parse.NewParser(tokens)
		expr, err := parser.ParseExpression()
		if err != nil {
			t.Fatalf("parser.ParseExpression() error = %v", err)
		}

		obj, err := EvalExpression(expr, env)
		if err != nil {
			t.Fatalf("Eval() error = %v", err)
		}
		if obj.Inspect() != tt.want {
			t.Errorf("Eval() = %v, want %v", obj.Inspect(), tt.want)
		}
	}
}

func TestPairAndListOperations(t *testing.T) {
	tests := []struct {
		input string
		want  string
	}{
		{"(cons 1 2)", "(1 . 2)"},
		{"(car (cons 1 2))", "1"},
		{"(cdr (cons 1 2))", "2"},
		{"(define x (cons 1 2)) (car x)", ""}, // define returns null
		{"(define y (cons 1 2)) (cdr y)", ""}, // define returns null
	}

	env := obj.NewEnvironment()
	RegisterBuiltinProcedures(env)

	for _, tt := range tests {
		lexer := lex.NewLexer(tt.input)
		tokens := lexer.ReadTokens()
		parser := parse.NewParser(tokens)
		exprs, err := parser.ParseProgram()
		if err != nil {
			t.Fatalf("parser.ParseProgram() error = %v", err)
		}

		var result string
		for i, expr := range exprs {
			obj, err := EvalExpression(expr, env)
			if err != nil {
				t.Fatalf("Eval() error = %v", err)
			}
			if i > 0 {
				result += " "
			}
			result += obj.Inspect()
		}

		// Special handling for define expressions
		if len(exprs) > 1 && tt.want == "" {
			// For define expressions, we only check the last expression result
			continue
		}

		if result != tt.want {
			t.Errorf("Eval() = %v, want %v", result, tt.want)
		}
	}
}

func TestCondIntegration(t *testing.T) {
	tests := []struct {
		input string
		want  string
	}{
		{
			input: "(cond ((> 5 3) 'bigger) ((< 5 3) 'smaller))",
			want:  "bigger",
		},
		{
			input: "(cond ((> 5 10) 'bigger) ((< 5 10) 'smaller) (else 'equal))",
			want:  "smaller",
		},
		{
			input: "(cond ((= 5 5) (+ 2 3)) ((> 5 3) (- 10 4))) ",
			want:  "5",
		},
		{
			input: "(define x 10) (cond ((> x 5) (* x 2)) ((< x 5) (/ x 2))) ",
			want:  "20",
		},
		{
			input: "(cond (#f 1))",
			want:  "",
		},
	}

	env := obj.NewEnvironment()
	RegisterBuiltinProcedures(env)

	for _, tt := range tests {
		lexer := lex.NewLexer(tt.input)
		tokens := lexer.ReadTokens()
		parser := parse.NewParser(tokens)
		exprs, err := parser.ParseProgram()
		if err != nil {
			t.Fatalf("parser.ParseProgram() error = %v", err)
		}

		var result obj.Object
		for _, expr := range exprs {
			result, err = EvalExpression(expr, env)
			if err != nil {
				t.Fatalf("Eval() error = %v", err)
			}
		}

		if result.Inspect() != tt.want {
			t.Errorf("Eval() = %v, want %v", result.Inspect(), tt.want)
		}
	}
}

// TestCommentIntegration 测试注释在整个解释器中的工作情况
func TestCommentIntegration(t *testing.T) {
	tests := []struct {
		input string
		want  string
	}{
		// 测试行尾注释
		{
			input: "(+ 1 2); this is a comment",
			want:  "3",
		},
		// 测试多行注释
		{
			input: "; This is a comment\n(+ 1 2)",
			want:  "3",
		},
		// 测试代码中间的注释
		{
			input: "(+ 1 ; comment\n2)",
			want:  "3",
		},
		// 测试多个注释
		{
			input: "; Comment 1\n; Comment 2\n(+ 1 2)",
			want:  "3",
		},
		// 测试注释与define一起使用
		{
			input: "(define x 5); define a variable\n(+ x 3)",
			want:  "8",
		},
		// 测试注释与复杂表达式一起使用
		{
			input: "(define (add x y) ; define a function\n  (+ x y)); body of the function\n(add 4 5)",
			want:  "9",
		},
	}

	env := obj.NewEnvironment()
	RegisterBuiltinProcedures(env)

	for _, tt := range tests {
		lexer := lex.NewLexer(tt.input)
		tokens := lexer.ReadTokens()
		parser := parse.NewParser(tokens)
		exprs, err := parser.ParseProgram()
		if err != nil {
			t.Fatalf("parser.ParseProgram() error = %v", err)
		}

		var result obj.Object
		for _, expr := range exprs {
			result, err = EvalExpression(expr, env)
			if err != nil {
				t.Fatalf("Eval() error = %v", err)
			}
		}

		if result.Inspect() != tt.want {
			t.Errorf("Eval() with comments = %v, want %v\nInput: %q", result.Inspect(), tt.want, tt.input)
		}
	}
}
